Wednesday, January 10, 2018

Juicy-footprint: An SMD Footprint designer DSL


 

Introduction 

 

Juicy-footprint is a domain specific language written in/for Java and Scala to design/reconstruct SMD footprints from the Recommended PCB Layout of the datasheet of an SMD component. Recommended PCB Layouts are usually given as engineering drawings where the distances between the parts are relative to each other. Most EDA applications, however, e.g. Eagle, requires SMD footprints to be given in absolute coordinates.

Figuring out absolute coordinates from engineering drawings can be exhausting and error-prone. Juicy-footprint is designed to help with resolving this impedance mismatch. The drawings can be directly represented with the Juicy-footprint DSL, which, when executed, provides absolute coordinates and displays the footprint.

The source code of the library and samples for Java and Scala can be found at https://github.com/domoszlai/juicy-footprint.

  

How it works

 

With juicy-footprint one creates shapes and defines relations between their properties (called constraints). The available shapes and their properties are the following:
  • Variable: relations can be defined between variables
  • Point: x, y: Variable
  • HorizontalLine, VerticalLine: p1, p2: Point; length: Variable
  • Rect: top, bottom: HorizontalLine; left, right: VerticalLine; width, height: Variable
  • Hole: top, bottom, left, right, center: Point; radius: Variable
    • Pad: topLeft, topRight, bottomLeft, bottomRight, center, centerTop, centerBottom, centerLeft, centerRight: Point; width, height: Variable
    The constraints must be linear, only addition and multiplication with a constant are allowed. The relations between the properties of a shape are pre-defined by the layout engine. So you do not have to tell the engine, e.g.,  how to calculate the center point from the topLeft and bottomRight coordinates. The library solves the linear equation system defined by the constraints to find absolute coordinates for all the points.

    In the following, instead of defining the API dryly, I rather show step-by-syep how to recreate our introductory layout (a micro USB connector) in Scala to get a taste of the library.

     

    A tutorial

     

    The source code of this example can be found at githab.com for Scala and Java.

    The very fist thing to do is to instantiate the layout engine that will be used later on to create objects and define constraints:
    val l = new Layouter();
    Then we can create the first shapes
    // 1.5 is a guess, could not find in the spec
    val a = l.createPad("A", 1.5, 1);
    val b = l.createPad("B", a.width, a.height);
    
    and define their relations:
    b.centerTop ~= a.centerTop + (6.4, 0);
    Next step, create the five pads for the actual connectors
    val p1 = l.createPad("P1", 0.4, 1.35);
    val p2 = l.createPad("P2", 0.4, 1.35);
    val p3 = l.createPad("P3", 0.4, 1.35);
    val p4 = l.createPad("P4", 0.4, 1.35);
    val p5 = l.createPad("P5", 0.4, 1.35);
    and set their relative positions:
    p2.centerTop ~= p3.centerTop - (0.65, 0);
    p1.centerTop ~= p2.centerTop - (0.65, 0);
    p4.centerTop ~= p3.centerTop + (0.65, 0);
    p5.centerTop ~= p4.centerTop + (0.65, 0);
    These five pads are nicely placed just in between the two pads on the sides, A and B. It is enough to set a relation between P3 and A and B, the relative positions of the other Ps are already defined.
    p3.centerBottom ~= a.centerBottom + 
             ((b.centerBottom.x - a.centerBottom.x) / 2, 0);
    Create the left pad with the oblong hole ("C.Outside"). The hole is simulated with another pad ("C.Inside"). The constraints are pretty straightforward: (1) their center points are the same (2) the difference between the widths and heights is equal:
    val c_inner = l.createPad("C.Inner", 0.45, 1.55);
    val c_outer = l.createPad("C.Outer");
    c_outer.height ~= 2.15;
    
    c_outer.center ~= c_inner.center; // (1)
    c_outer.height - c_inner.height ~= 
               c_outer.width - c_inner.width; // (2)
    
    Now the right pad with the oblong hole, the exact same way:
    val d_inner = l.createPad("D.Inner", 0.45, 1.55);
    val d_outer = l.createPad("D.Outer");
    d_outer.height ~= 2.15;
    
    d_outer.center ~= d_inner.center; // (1)
    d_outer.height - d_inner.height ~= 
               d_outer.width - d_inner.width; // (2)
    
    Still needs to set (1) the relative position of C to D (2) the relative position of C and/or D to the other pads:
    d_inner.center ~= c_inner.center + (6.45, 0); // (1)
    
    // (2)
    d_inner.center.y ~= p5.centerTop.y + 3.35; 
    c_inner.center.y ~= p5.centerTop.y + 3.35;
    
    Horizontally, the distance between A and D (from the left) equals to the distance between B and C (from the right). The A.Center.X and D.Center.X seems different on the layout (also with B and C).
    d_outer.centerLeft.x - a.centerLeft.x ~= 
                   b.centerRight.x - c_outer.centerRight.x;
    
    Finally, the PCB edge line is created and positioned:
    val edge = l.createHorizontalLine("PCB edge");
    edge.p1 ~= a.bottomLeft + (0, 3.45);
    edge.length ~= b.bottomRight.x - a.bottomLeft.x;
    
    The very last step is to set an absolute coordinate for any of the points we have (practically the origin):
    a.topLeft ~= (0, 0);
    
    From this description, the layout engine is able to derive an absolute coordinate for all the points, and draw the layout:

    Share:

    Sunday, June 4, 2017

    V-plotter math: coordinate transformation with rotation compensation


    The most important thing to deal with in v-plotter software is the coordinate transformation. Descartes coordinates must be converted to the coordinate system of the plotter. In the most simple case, when the strings meet at one point of the carriage, this is a trivial task, straightforward application of the Pythagoras theorem.

    However, such a simple design comes with a price as the carriage becomes unstable. And this happens when one tries to stabilize it without adjusting the coordinate transformation:



    The reason for the distorted image lies in the rotation of the carriage that must be compensated to get the proper drawings:


    In the following I explain the method I came up with to compensate the rotation. This method is integrated with my v-plotter step generator, and there is also a prototype Java application available for educational purposes.

    Let us suppose that we have a v-plotter and the pen is in the center of the mass of its carriage. The task is to answer, given the \(C(x,y)\) coordinate, one of these three identical questions:

    • the rotation angle \((\gamma)\) of the box
    • the coordinates of the \(A\) and \(B\) points
    • the lengths of the strings.

    The idea is that for any given \(C(x,y)\) there must be exactly one \(\gamma\) that nature chooses, the angle where the sum of torques acting on the carriage is zero.

    First, for a given \(C(x,y)\) and \(\gamma\), we need to calculate the torques exerted on the carriage by the two strings. The algorithm consist of the following steps:

    1. Given \(C(x,y)\) and the dimensions of the carriage, calculate the \(A\) and \(B\) points (considering that the rotation is zero)
    2. Rotate the carriage around \(C\) by \(\gamma\) to get \(A'\) and \(B'\) 
    3. Calculate the \(\alpha\) and \(\beta\) angles
    4. Calculate the \(T_1\) and \(T_2\) tensions in the strings using the following formulas:

      For explanation, read the details at the end of the post.

      \[
      T_1 = \frac{F_g}{\cos \alpha \tan \beta + \sin \alpha}
      \qquad
      T_2 = T_1 \frac{\cos \alpha}{\cos \beta}
      \]
      where

      \(F_g = mg\), the mass of the carriage multiplied by the gravity constant.
    5. Calculate the force vectors:
      \[
      \vec{F_1} = \frac{\vec{S_1A'}}{\lvert\vec{S_1A'}\rvert}  T_1
      \qquad
      \vec{F_2} = \frac{\vec{S_2B'}}{\lvert\vec{S_2B'}\rvert}  T_2
      \]
    6. Calculate torques. Torque is the cross product of the force vector and the level arm vector:
      \[
      \tau_1 = \vec{A'C} \times \vec{F_1}
      \qquad
      \tau_2 = \vec{B'C} \times \vec{F_2}
      \]

    Finally, let's create a function that implements this algorithm and returns the sum of the torques:
    \[\tau(x,y)( \gamma) = \tau_1 + \tau_2\]
    As we need to find the angle where \(\tau_1 + \tau_2 = 0\), that is the carriage is in rotational equilibrium, the next step is to calculate the root of the \(\tau(x,y)\) function. This function is strictly monotone, thus Newton's iteration can be applied with an initial guess of \(0\) degrees.

    A prototype implementation can be found here, and if you are interested in the details of the tension equations, read further (and watch the lecture about the super hot tension problem):

    To find the tension in the strings, Newton's second law must be solved:
    \[a = \frac{\sum{F}}{m}\]
    Or rather these: acceleration is required to be zero and the equation is split, because it is easier to solve the vertical and horizontal projections one by one:
    \[0 = \frac{\sum{F_x}}{m}\]
    \[0 = \frac{\sum{F_y}}{m}\]
    First, the \(x\) direction:
    \[0 = \frac{T_{1x}-T_{2x}}{m}\]
    that is
    \[T_{1x} = T_{2x}\tag{1}\]
    The \(y\) direction is only a tiny bit more complicated as the force of gravity is also must be taken into account:
    \[0 = \frac{-F_g+T_{1y}+T_{2y}}{m}\]
    which is
    \[F_g = T_{1y}+T_{2y}\tag{2}\]
    The vertical and horizontal projections of \(T_1\) and \(T_2\) are simple like this (at this point the \(\alpha\) and \(\beta\) angles are already calculated):
    \[
    T_{1x} = T_1 \cos \alpha \tag{3}
    \]
    \[
    T_{2x} = T_2 \cos \beta \tag{4}
    \]
    \[
    T_{1y} = T_1 \sin \alpha \tag{5}
    \]
    \[
    T_{2y} = T_2 \sin \beta \tag{6}
    \]
    Now substitute \((3)\) and \((4)\) into \((1)\):
    \[T_1 \cos \alpha = T_2 \cos \beta\]
    From that:
    \[T_1 = T_2 \frac{\cos \beta}{\cos \alpha}\tag{7}\]
    Combining \((5)\) and  \((7)\), \(T_{1y}\) can be expressed now as:
    \[T_{1y} = T_2 \cos \beta \tan \alpha\tag{8}\]
    Substitute \((6)\) and \((8)\) into \((2)\):
    \[F_g = T_{2}(\cos \beta \tan \alpha + \sin beta)\tag{9}\] 
    The final equations can be derived directly from \((7)\) and \((9)\).



    Share:

    Sunday, March 5, 2017

    On the viability of semi-indoor solar energy harvesting

    Introduction


    This is the log of my recent experiment on the viability of solar powering of an IoT device in a semi-indoor environment (indoor, but bright enough to enable a plant growing, e.g. by a window). I basically wanted to find out what is the absolutely minimum power that can be squeezed out from a tiny 53x30mm, 5V, 30mA solar panel. The measurement took place in the Netherlands between January 10-26, when the amount of sunshine is the lowest in the year.

    The hardware


    Because I did not have a real load on the solar panel, a just measured the short-circuit current and the no-load voltage as an indication of the generated energy. The current was measured by an INA219 device and was logged by an Intel Edison Arduino Breakout Kit, the voltage was simply measured by one of the analog pins of the Edison (the used software is available at github).

    However,  the short-circuit current and the no-load voltage cannot be measured in the same time as voltage drops to nearly zero when the short-circuit current is measured. To overcome this, I used a RFP30N06LE TTL level N-Channel MOSFET to connect and disconnect the INA219 from the circuit.

    The Edison board has a 10-bit analog to digital converter, it maps input voltages between 0 and 5 volts into integer values between 0 and 1023, having a resolution of 4.9 mV. The INA219 was used in a div8 mode to have a resolution of 0.1 mA.


    The result


    During the measurement, the solar panel never gave more than 4V or 0.5mA, and it generated approximately 10 mA all together in this two weeks period.




    Conclusion


    The result sounds absolutely terrible regarding that the panel could generate 30mA in ideal conditions. However, this is an absolutely worst case scenario, and it still would generate 20 mA a month, 240 mA a year (probably it gives this amount of power during a sunny summer day). In theory, this is certainly enough to charge e.g. a 150 mA LiPo battery in a year, which sounds ridiculous, but can be acceptable for many low power applications.

    Reality can be quite different though. The solar panel gave really small amount of power, a way below 1 mA. The question is if it is enough to drive a LiPo charger chip, and how much energy the chip itself uses up... In a next post I try to find a solar harvesting chip with a very low quiescent current to see how much of the generated energy can be actually stored in a battery.


    Share:

    Sunday, January 8, 2017

    Arduino capacitive proximity sensor with a square-wave signal generator

    Introduction


    In this post I explain a capacitive proximity / touch sensor for Arduino based on a square wave generator rather than charge time measurement as the popular CapSense library. This design results in very stable sensor readings and it also works from a battery. It is compatible with virtually every Arduino board and even with most ATtiny chips.



    How it works

     

    It uses the very same technique as the beautiful musical instrument, the theremin, only it operates on a much lower frequency (around ~60KHz instead of some MHz) and uses square-wave signals for the sake of easier counting. So let me paraphrase a simple explanation of its working principle from theremin.info:
    By using an alternating current of suitable frequency, tones of varying pitch are easily obtainable. A small vertical rod is used as the antenna. When the instrument is in operation, electro-magnetic waves of very weak energy are generated around this rod. These waves are of a definite length and frequency. The approach of a hand, which is an electrical conductor, alters the conditions in the electro-magnetic field surrounding the antenna, changes its capacity and thus affects the frequency of the alternating current generated by the apparatus. In this manner, a kind of invisible touch is produced in the space surrounding the antenna, and, as in a cello, a finger pressing on a string produces a higher pitch as it approaches the bridge, in this case also, the pitch increases as the finger is brought nearer the antenna. 
    In summary, stable, ~60KHz square-waves are generated by an RC oscillator. The capacity of an approaching hand causes a slight drop in this frequency what is detected by an Arduino based frequency counting library.

    The square-wave generator

     

    The sensor uses a Schmitt trigger based square-wave generator; this was the simplest suitable circuit I could find for this purpose, it consists only of three very cheap, commonly available parts: a capacitor, a resistor and a 7414 Schmitt trigger chip. The frequency of the output signal can be calculated by the following formula:  

    freq = 1.2 / (R*C)

    With some experiments, I found that 60Khz base frequency fits nicely my initial goal of starting to sense around ~15cm. I achieved this by choosing C = 100pF and R = 200KΩ.

    To make the oscillator stable in the long term, it may make sense to use a ceramic capacitor with a low temperature drift, a so called NP0 type.

    In summary, the part list and a lovely Fritzing diagram for the whole sensor:
    • C1 = 100 pF
    • R   = 200 KΩ
    • IC  = 74HC14 Hex inverting Schmitt trigger 
    • C2 = 100nF (optional, but recommended)

    The software


    The following code can be downloaded directly from https://github.com/domoszlai/arduino-proximity-sensor

    In a perfect world, all one should do is to constantly measure the frequency of the oscillator and compare it to the expected (60KHz) frequency.  At proximity, the frequency drop is small (some hundreds Hz) and more or less linear with the distance. At touch, there is a huge, sudden, non-linear 
    frequency drop. With some experimentation one can easily determine these frequencies to get an idea how to interpret an actual frequency measurement.   

    However, we do not live in a perfect world:
    • The actual value of a resistor or capacitor is precise only to the extent of some tolerance (so you never get 60Khz, only if you buy high precision parts)
    • The actual value of a capacitor is dependent on the current temperature, voltage, ... (e.g. the frequency is drifting with time) 
    • The actual frequency is influenced by environmental noise (just put the antenna closer to your laptop to verify this)

    To get proper, stable result, these problems must be handled. Probably there is no one, ultimate solution for these issues, rather, the solution vary from application to application. For my purposes, I found the following heuristic satisfying:

    Instead of using the measured frequency directly, two lines of frequency measurements are maintained through an exponential filter. A baseline, with a huge weight (0.995), and current line with a smaller weight (0.75). Their values are initialized in the setup().

    float current_line;
    float baseline;
    
    void setup()
    {
      // https://github.com/domoszlai/arduino-frequency-counter
      current_line = baseline = count_frequency();
    }
    
    void loop()
    {
      unsigned long f = count_frequency();
    
      current_line = current_line * CL_WEIGHT + (1 - CL_WEIGHT) * f;
      baseline = baseline * BL_WEIGHT + (1 - BL_WEIGHT) * f;
    
      ...
    }
    
    
    The baseline contains the base frequency, the frequency expected when proximity is not sensed. However, the baseline is not static: because of the exponential filter, it very slowly follows the changes of the current frequency measurement. This way, drifting of the base frequency is handled. It has, however, the side effect, that the system slowly adapts to proximity...

    The current line also follows the changes of the current frequency measurement, but, because of the smaller weight, in a much faster pace. In this case, the exponential filter is applied only to smooth the frequency measurement.

    A relative distance of the proximity can be computed easily now by proportionate the difference of the baseline and the current line to the maximal expected frequency drop:

    // distance is reciprocal, bigger value indicates smaller distance
    // and its range is 0-255 
    int diff = max(0, base_line - current_line);
    if(diff < MAX_EXPECTED_FREQ_NOISE) diff = 0;
    int distance = diff * 255L / MAX_FREQUENCY_DROP;
    distance = min(distance, 255);


    Finally, putting everything together:


    // https://github.com/domoszlai/arduino-frequency-counter
    #include "frequency_counter_TC.h"
    
    //#define DEBUG
    
    #define MAX_FREQUENCY_DROP        500
    #define MAX_EXPECTED_FREQ_NOISE   30
    #define CL_WEIGHT                 0.75
    #define BL_WEIGHT                 0.995
    
    float current_line = 0;
    float baseline = 0;
    
    void setup()
    {
    #ifdef DEBUG
      Serial.begin(9600);
    #endif
    
      current_line = baseline = count_frequency();
    }
    
    void loop()
    {
      unsigned long f = count_frequency();
    
      current_line = current_line * CL_WEIGHT + (1 - CL_WEIGHT) * f;
      baseline = baseline * BL_WEIGHT + (1 - BL_WEIGHT) * f;
    
      // distance is reciprocal, bigger value indicates smaller distance
      // and its range is 0-255 
      int diff = max(0, baseline - current_line);
      if(diff < MAX_EXPECTED_FREQ_NOISE) diff = 0;
      int distance = diff * 255L / MAX_FREQUENCY_DROP;
      distance = min(distance, 255);
    
    #ifdef DEBUG
      Serial.print(distance);
      Serial.print(" ");
      Serial.print(f);
      Serial.print(" ");
      Serial.print(baseline);
      Serial.print(" ");
      Serial.println(current_line);   
    #endif  
    }
    

    Implementation notes

    • The oscillator is prone to the instability of the input voltage. A standard USB 5V can be quite noisy; in  my case, switching to regulated 3.3V reduced the frequency deviation by 50% the least. A voltage regulator is especially important, if it runs on a battery where voltage may drop as the load increases
    • Avoid breadboards. The parasitic capacitance of an average breadboard messes up the frequency spectrum so much, it is hard see the peaks
    • If ATtiny is used as frequency counter, it is essential to use an at least 8MHz external crystal. For a frequency counter, a stable timebase is essential; for this purpose, the internal oscillator of an ATtiny is just not good enough (this video is gold on the stability of oscillators)
    • If you want to increase sensitivity, try increase the base frequency (and the size of the antenna). In this case, you might want to try an LC oscillator instead of the RC one, as they tend to be more stable
    • It also make sense to decrease sensitivity, especially for a touch sensor. In this case 15-20Khz base frequency must be enough

     




    Share:

    Wednesday, January 4, 2017

    Arduino frequency counter experiments

    Introduction 

     

    In this post, I would like to summarize my recent experiments with different frequency counting approaches using the Arduino platform. My original goal is to develop a small, capacitive proximity sensor, that is more reliable than the default charge time measurement based one (and just as importantly, works on a battery). The idea is, similarly to a theremin, to use an oscillator to generate square waves and detect small changes in the frequency caused by the proximity of a person. For this, I needed a reliable frequency counter first. My requirements were the following:
    • it should be as precise as possible for at least up to 100KHz
    • it should work on an ATtiny (even on an ATtiny13)
    UPDATE: check out the proximity sensor, this library is developed for: http://dlacko.blogspot.com/2017/01/arduino-capacitive-proximity-sensor.html

     

    Results


    I ended up with a small library that is able to count frequency up to the MHz range on the more complex platforms (e.g. Uno, Mega), and I also got it working on most of the ATtiny ones (I tested it on a ATtiny85), but not on the ATtiny13 (technically it would be possible, but I could not fit the proximity sensor code into the 1K flash memory, so I decided not to waste more time on it). On an ATTiny, the inaccuracy of the internal oscillator may affect the measurement with a constant factor, but this is fine for my application (why would you count frequency with an ATTiny anyway?)

    The library uses fixed 100ms gate time (the time period of counting pulses), which introduces some error (the pulse count is multiplied by 10 to get the frequency, thus, the last digit is always 0). Because of this, it is also worth to mention that this library does not work well for very low frequencies.

    The frequency counter library is available at https://github.com/domoszlai/arduino-frequency-counter. It implements two different approaches as they both have pros and cons, and a separate one for ATTiny. Implementation details can be found in the source code and on the github page.

    Approaches

     

    I identified three basic frequency counting methods based on the number of  required hardware timers:

    • No timer

      This is a very naive approach, and it is not even entirely timer less as it implicitly uses Timer0 for counting time. Theses methods are usually based on one of the pulseIn(), millis()micros()functions to measure gate time or pulse width. I found them too imprecise for my purposes.
    • 1 timer
      This method uses one timer for gate time measurement and a pin-change interrupt (PCI) for counting the pulses. It is very generally usable as pin-change interrupts are available for many/most pins, but it may work well for lower frequencies only (it worked perfectly with ~60KHz in my tests). This is the method used by the ATTiny counter as the smallest ones, e.g. ATTiny85, has only one available timer (two but, Timer0 is used by the Arduino core).

      See: frequency_counter_PCI.cpp
    • 2 timers

      I found this the most reliable method that works for up to several MHz. It uses one timer for gate time measurement and a hardware timer/counter (TC) for counting the pulses. However, the hardware counter requires the usage of one specific pin, what can be very impractical in some situations. It must be the T1 pin (usually pin 5) for most boards, but T5 (pin 47) in the case of an Arduino Mega.

      See: frequency_counter_TC.cpp



    Share:

    Saturday, December 17, 2016

    Breadboard and perfboard diagrams of an MCP73831 based Li-Ion, Li-Polymer battery charger

    One of my ongoing projects is a very low-power electronics device that supposedly runs on a single-cell Li-Ion or Li-Polymer battery for a very long time, hopefully many-many months. In my idea, the battery actually could be a rechargeable one, e.g. LIR2032 or LIR2450 button cell, and I might also be able to recharge them during these months from a small solar panel, so ideally it runs "forever" with a single battery.

    I looked for charger circuits and found out there are many incredibly cheap single-chip solutions available. For some reason I do not remember any more, I picked the MCP73831 chip, which is a very small, single-cell Li-Ion, Li-Polymer battery charger IC.

    To be able to experiment with the chip, I just took the typical application schematics from the specification and created breadboard and perfboard diagrams. In this post I just want to share these diagrams as they maybe useful for others as well.


    Please note that this circuit has no low voltage protection. On one hand, it is very practical as you can use it to restore over-discharged batteries. On the other hand, you should be careful with such batteries. Please read this blog post about recharging over-discharged LiPo batteries.


    This chip is coming in a SOT23-5 package, so it is actually too small for prototyping; first of all, it must be converted to DIP format using a SOT23 to DIP adapter board. I used a DIP10 one, because that was at home, but there are other, smaller variants (less legs) as well.


    The diagrams are slightly modified. First of all, I replaced the 2K resistor with a 10K one, so it has 100mA output instead of 500mA. I also added one more LED indicating incoming power. So, the green LED is always on, the red LED indicates the chargring status.

    • Blinking: no battery / battery fault
    • On: battery is being charged
    • Off: battery is fully charged 

    I created Fritzing diagrams, and also a DIY Layour Creator one for the perfboard diagram as I find it nicer than the Fritzing one:

    MCP73831 LiPo charger Fritzing diagram

    MCP73831 LiPo charger perfboard diagram


    And finally, the realization. Two remarks: 1. I added some more connectors to the perfboard to make it more useful 2. A single-sided perfboard would be sufficient, and would look nicer, but I had only the double-sided at home

    MCP73831  LiPo charger breadboard

    MCP73831  LiPo charger perfboard


    Share:

    Thursday, November 3, 2016

    Markdown rendering issues on Hackage

    Just a quick post on some Markdown rendering issues I recently ran into on Hackage. They were very annoying as the markup was properly rendered on github, and I could not spot any obvious problems.

    1. First header is rendered as the original markup


    You get

              ## synopsis

    instead of

      Synopsis


    Solution: there is an invisible Unicode byte order mark in the beginning of the file. Seemingly Hackage does not like Unicode.


    2. Subsequent lists are merged


    You have a markup like this:

    * list1_item1
    * list1_item2

    Something in between

    * list2_item1
    * list2_item2
     

    Something to the end
    But you get something like this:

    • list1_item1
    • list1_item2  Something in between
    • list2_item1
    • list2_item2  Something to the end

    Solution: you have Windows line endings. Obviously Hackage does not like Windows line endings.


    Share:

    Featured Post

    Juicy-footprint: An SMD Footprint designer DSL

      Introduction    Juicy-footprint is a domain specific language written in/for Java and Scala to design/reconstruct SMD footprint...

    Popular Posts